home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / doom / tw212_sv.zip / TWQWSRC / CLIENT.QC < prev    next >
Text File  |  1997-05-07  |  56KB  |  1,856 lines

  1.  
  2. // prototypes
  3. void () W_WeaponFrame;
  4. void() W_SetCurrentAmmo;
  5. void() player_pain;
  6. void() player_stand1;
  7. void (vector org) spawn_tfog;
  8. void (vector org, entity death_owner) spawn_tdeath;
  9. void() SpawnRunes;
  10.  
  11. float   modelindex_eyes, modelindex_player;
  12. float pregameover;
  13.  
  14. // ZOID: with several effects doing the dimlight thing, they just can't
  15. // turn it off.  Do not set self.effects with EF_DIMLIGHT directly.  This
  16. // will automatically do it when CheckPowerups is called
  17. // EF_DIMLIGHT is used;
  18. // 1. Invincible (Pentagram)
  19. // 2. Super Damage (Quad Power)
  20. // 3. Having Flag in Capture
  21. // self is player
  22. void () CheckDimLight = {
  23.     local float flag;
  24.  
  25.     flag = 0;
  26.     // invincible ThunderWalker: Fixed spelling, was invincable
  27.     if (self.invincible_finished > time)
  28.         flag = 1;
  29.     // quad
  30.     if (self.super_damage_finished > time)
  31.         flag = 1;
  32.     // flag
  33.     if (self.player_flag & ITEM_ENEMY_FLAG)
  34.         flag = 1;
  35.  
  36.     if (flag)
  37.         self.effects = self.effects | EF_DIMLIGHT;
  38.     else
  39.         self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  40. };
  41.  
  42. /*
  43. =============================================================================
  44.  
  45.                 LEVEL CHANGING / INTERMISSION
  46.  
  47. =============================================================================
  48. */
  49.  
  50. string nextmap;
  51.  
  52. float   intermission_running;
  53. float   intermission_exittime;
  54.  
  55. /*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16)
  56. This is the camera point for the intermission.
  57. Use mangle instead of angle, so you can set pitch or roll as well as yaw.  'pitch roll yaw'
  58. */
  59. void() info_intermission =
  60. {
  61. };
  62.  
  63.  
  64.  
  65. void() SetChangeParms =
  66. {
  67.     if (self.health <= 0)
  68.     {
  69.         SetNewParms ();
  70.         if (gamestart)
  71.             parm10 = -1;
  72.         else
  73.             parm10 = self.steam; // Save the current team of the player
  74.         parm14 = self.statstate;
  75.         return;
  76.     }
  77.  
  78. // remove items
  79.     self.items = self.items - (self.items & 
  80.     (IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );
  81.     
  82. // cap super health
  83.     if (self.health > 100)
  84.         self.health = 100;
  85.     if (self.health < 50)
  86.         self.health = 50;
  87.     SetNewParms();
  88. // *TEAMPLAY*
  89.     if (gamestart)
  90.         parm10 = -1;
  91.     else
  92.         parm10 = self.steam; // Save the current team of the player
  93.     parm14 = self.statstate;
  94.     parm16 = self.player_flag;
  95.  
  96. };
  97.  
  98. void() SetNewParms =
  99. {
  100.     if (gamestart && !pregameover) {
  101.         parm1 = IT_AXE;
  102.         parm2 = 100;
  103.         parm4 = 0;
  104.         parm8 = IT_AXE;
  105.         parm10 = 1;
  106.     } else {
  107.         if (cvar("teamplay") & TEAM_DISABLE_GRAPPLE)
  108.             parm1 = IT_SHOTGUN | IT_AXE | IT_AIRGUN;
  109.         else
  110.             parm1 = IT_SHOTGUN | IT_AXE | IT_GRAPPLE | IT_AIRGUN;
  111.         parm2 = 100;
  112.         parm3 = 50;
  113.         parm9 = 30;
  114.         parm1 = parm1 + IT_ARMOR1;
  115.         parm4 = 40;
  116.         parm8 = IT_SHOTGUN;
  117.         parm10 = -1;    // Reset 
  118.     }
  119.     parm5 = 0;
  120.     parm6 = 0;
  121.     parm7 = 0;
  122.     parm8 = IT_SHOTGUN;
  123. // *TEAMPLAY*
  124.     parm10 = -1;    // Reset 
  125.     parm14 = self.statstate;
  126.     parm16 = 0;
  127. };
  128.  
  129. void() DecodeLevelParms =
  130. {
  131.     self.player_flag = self.player_flag | parm16;
  132.     self.player_flag = self.player_flag - (self.player_flag & ITEM_RUNE_MASK);
  133.     self.player_flag = self.player_flag - (self.player_flag & ITEM_ENEMY_FLAG);
  134.     self.statstate = parm14;
  135.  
  136.     if (gamestart) {
  137.         SetNewParms ();     // take away all stuff on starting new episode
  138.         self.ctfskinno = 0;
  139.     } else {
  140.         self.ctfskinno = (self.player_flag & 65280) / 256;
  141.         TeamSkinSet();
  142.     }
  143.  
  144.     self.items = parm1;
  145.     self.health = parm2;
  146.     self.armorvalue = parm3;
  147.     self.ammo_shells = parm4;
  148.     self.ammo_nails = parm5;
  149.     self.ammo_rockets = parm6;
  150.     self.ammo_cells = parm7;
  151.     self.weapon = parm8;
  152.     self.armortype = parm9 * 0.01;
  153.  
  154.     // *XXX* EXPERT CTF
  155.  
  156.     // Reset times for additional scoring system on level change and server join
  157.     // dprint("decode level parms\n");
  158.     self.last_returned_flag = -10;
  159.     self.last_fragged_carrier = -10;
  160.     self.flag_since = -10;
  161.     self.last_hurt_carrier = -10;
  162.  
  163. // *TEAMPLAY*
  164.     if (TeamColorIsLegal(parm10))
  165.         self.steam = parm10;
  166. };
  167.  
  168. /*
  169. ============
  170. FindIntermission
  171.  
  172. Returns the entity to view from
  173. ============
  174. */
  175. entity() FindIntermission =
  176. {
  177.     local   entity spot;
  178.     local   float cyc;
  179.  
  180. // look for info_intermission first
  181.     spot = find (world, classname, "info_intermission");
  182.     if (spot)
  183.     {   // pick a random one
  184.         cyc = random() * 4;
  185.         while (cyc > 1)
  186.         {
  187.             spot = find (spot, classname, "info_intermission");
  188.             if (!spot)
  189.                 spot = find (spot, classname, "info_intermission");
  190.             cyc = cyc - 1;
  191.         }
  192.         return spot;
  193.     }
  194.  
  195. // then look for the start position
  196.     spot = find (world, classname, "info_player_start");
  197.     if (spot)
  198.         return spot;
  199.     
  200.     objerror ("FindIntermission: no spot");
  201. };
  202.  
  203.  
  204. void() GotoNextMap =
  205. {
  206.     if (cvar("samelevel"))  // if samelevel is set, stay on same level
  207.         changelevel (mapname);
  208.     else {
  209. //FIXME special case for now
  210.         if (nextmap == "end")
  211.             nextmap = "dm1";
  212.         else if (nextmap == "ctf9")
  213.             nextmap = "ctf2m1";
  214.  
  215.         changelevel (nextmap);
  216.     }
  217. };
  218.  
  219.  
  220.  
  221. /*
  222. ============
  223. IntermissionThink
  224.  
  225. When the player presses attack or jump, change to the next level
  226. ============
  227. */
  228. void() IntermissionThink =
  229. {
  230.     if (time < intermission_exittime)
  231.         return;
  232.  
  233.     if (!self.button0 && !self.button1 && !self.button2)
  234.         return;
  235.     
  236.     GotoNextMap ();
  237. };
  238.  
  239. /*
  240. ============
  241. execute_changelevel
  242.  
  243. The global "nextmap" has been set previously.
  244. Take the players to the intermission spot
  245. ============
  246. */
  247. void() execute_changelevel =
  248. {
  249.     local entity    pos;
  250.  
  251.     intermission_running = 1;
  252.     
  253. // enforce a wait time before allowing changelevel
  254.     intermission_exittime = time + 8;
  255.  
  256.     pos = FindIntermission ();
  257.  
  258. // play intermission music
  259.     WriteByte (MSG_ALL, SVC_CDTRACK);
  260.     WriteByte (MSG_ALL, 3);
  261.  
  262.     WriteByte (MSG_ALL, SVC_INTERMISSION);
  263.     WriteCoord (MSG_ALL, pos.origin_x);
  264.     WriteCoord (MSG_ALL, pos.origin_y);
  265.     WriteCoord (MSG_ALL, pos.origin_z);
  266.     WriteAngle (MSG_ALL, pos.mangle_x);
  267.     WriteAngle (MSG_ALL, pos.mangle_y);
  268.     WriteAngle (MSG_ALL, pos.mangle_z);
  269.     
  270.     other = find (world, classname, "player");
  271.     while (other != world)
  272.     {
  273.         other.takedamage = DAMAGE_NO;
  274.         other.solid = SOLID_NOT;
  275.         other.movetype = MOVETYPE_NONE;
  276.         other.modelindex = 0;
  277.         other = find (other, classname, "player");
  278.     }   
  279.  
  280. };
  281.  
  282.  
  283. void() changelevel_touch =
  284. {
  285.     local entity    pos;
  286.  
  287.     if (other.classname != "player")
  288.         return;
  289.  
  290. // if "noexit" is set, blow up the player trying to leave
  291.     if (teamplay & TEAM_CAPTURE_FLAG)
  292.         return;
  293.  
  294.     if ((cvar("samelevel") == 2) || ((cvar("samelevel") == 3) && !gamestart))
  295.         return; // do nothing
  296.  
  297.     bprint (PRINT_HIGH, other.netname);
  298.     bprint (PRINT_HIGH," exited the level\n");
  299.     
  300.     nextmap = self.map;
  301.  
  302.     SUB_UseTargets ();
  303.  
  304.     self.touch = SUB_Null;
  305.  
  306. // we can't move people right now, because touch functions are called
  307. // in the middle of C movement code, so set a think time to do it
  308.     self.think = execute_changelevel;
  309.     self.nextthink = time + 0.1;
  310. };
  311.  
  312. /*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION
  313. When the player touches this, he gets sent to the map listed in the "map" variable.  Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats.
  314. */
  315. void() trigger_changelevel =
  316. {
  317.     if (gamestart) {
  318.         if (self.map == "e1m1")
  319.             self.message = "E1 Dimension of the Doomed";
  320.         else if (self.map == "e2m1")
  321.             self.message = "E2 The Realm of Black Magic";
  322.         else if (self.map == "e3m1")
  323.             self.message = "E3 The Netherworld";
  324.         else if (self.map == "e4m1")
  325.             self.message = "E4 The Elder World";
  326.         else if (self.map == "end")
  327.             self.message = "The Deathmatch Arenas";
  328.         else
  329.             self.message = "Unknown";
  330.         self.classname = "trigger_voteexit";
  331.         trigger_voteexit();
  332.         return;
  333.     }
  334.  
  335.     if (!self.map)
  336.         objerror ("chagnelevel trigger doesn't have map");
  337.     
  338.     InitTrigger ();
  339.     self.touch = changelevel_touch;
  340. };
  341.  
  342.  
  343. /*
  344. =============================================================================
  345.  
  346.                 PLAYER GAME EDGE FUNCTIONS
  347.  
  348. =============================================================================
  349. */
  350.  
  351. void() set_suicide_frame;
  352.  
  353. // called by ClientKill and DeadThink
  354. void() respawn =
  355. {
  356.     // make a copy of the dead body for appearances sake
  357.     CopyToBodyQue (self);
  358.     // set default spawn parms
  359.     SetNewParms ();
  360.     // respawn      
  361.     PutClientInServer ();
  362. };
  363.  
  364.  
  365. /*
  366. ============
  367. ClientKill
  368.  
  369. Player entered the suicide command
  370. ============
  371. */
  372. void() ClientKill =
  373. {
  374.     if (gamestart) {
  375.         sprint(self, PRINT_HIGH, "Life just started.\n");
  376.         return;
  377.     }
  378.  
  379.     if (self.suicide_count > 3) {
  380.         sprint(self, PRINT_HIGH, "You have suicided too much already.\n");
  381.         return;
  382.     }
  383.     bprint (PRINT_MEDIUM, self.netname);
  384.     bprint (PRINT_MEDIUM, " suicides\n");
  385.     DropRune();
  386.     TeamCaptureDropFlagOfPlayer(self);
  387.     set_suicide_frame ();
  388.     self.modelindex = modelindex_player;
  389.     logfrag (self, self);
  390.     self.frags = self.frags - 2;    // extra penalty
  391.     self.suicide_count = self.suicide_count + 1;
  392.     respawn ();
  393. };
  394.  
  395. float(vector v) CheckSpawnPoint =
  396. {
  397.     return FALSE;
  398. };
  399.  
  400. /*
  401. ============
  402. SelectSpawnPoint
  403.  
  404. Returns the entity to spawn at
  405. ============
  406. */
  407. entity() SelectSpawnPoint =
  408. {
  409.     local   entity spot, thing;
  410.     local   float   pcount;
  411.  
  412. // testinfo_player_start is only found in regioned levels
  413.     spot = find (world, classname, "testplayerstart");
  414.     if (spot)
  415.         return spot;
  416.         
  417. // choose a info_player_deathmatch point
  418. //CTF spawns
  419.     if (!self.killed) {
  420.         spot = TeamCaptureSpawn();
  421.         if (spot != world) 
  422.             return spot;
  423.     } else if (gamestart && self.killed) {
  424.         lastvotespawn = find(lastvotespawn, classname, "info_vote_destination");
  425.         if (lastvotespawn == world)
  426.             lastvotespawn = find(lastvotespawn, classname, "info_vote_destination");
  427.         return lastvotespawn;
  428.     }
  429.     lastspawn = find(lastspawn, classname, "info_player_deathmatch");
  430.     if (lastspawn == world)
  431.         lastspawn = find (lastspawn, classname, "info_player_deathmatch");
  432.     if (lastspawn != world)
  433.         return lastspawn;
  434.     
  435.     spot = find (world, classname, "info_player_start");
  436.     if (!spot)
  437.         error ("PutClientInServer: no info_player_start on level");
  438.     
  439.     return spot;
  440. };
  441.  
  442. void() DecodeLevelParms;
  443. void() PlayerDie;
  444.  
  445. /*
  446. ===========
  447. ValidateUser
  448.  
  449.  
  450. ============
  451. */
  452. float(entity e) ValidateUser =
  453. {
  454. /*
  455.     local string    s;
  456.     local string    userclan;
  457.     local float rank, rankmin, rankmax;
  458.  
  459. //
  460. // if the server has set "clan1" and "clan2", then it
  461. // is a clan match that will allow only those two clans in
  462. //
  463.     s = serverinfo("clan1");
  464.     if (s)
  465.     {
  466.         userclan = masterinfo(e,"clan");
  467.         if (s == userclan)
  468.             return true;
  469.         s = serverinfo("clan2");
  470.         if (s == userclan)
  471.             return true;
  472.         return false;
  473.     }
  474.  
  475. //
  476. // if the server has set "rankmin" and/or "rankmax" then
  477. // the users rank must be between those two values
  478. //
  479.     s = masterinfo (e, "rank");
  480.     rank = stof (s);
  481.  
  482.     s = serverinfo("rankmin");
  483.     if (s)
  484.     {
  485.         rankmin = stof (s);
  486.         if (rank < rankmin)
  487.             return false;
  488.     }
  489.     s = serverinfo("rankmax");
  490.     if (s)
  491.     {
  492.         rankmax = stof (s);
  493.         if (rankmax < rank)
  494.             return false;
  495.     }
  496.  
  497.     return true;
  498. */
  499. };
  500.  
  501.  
  502. /*
  503. ===========
  504. PutClientInServer
  505.  
  506. called each time a player enters a new level
  507. ============
  508. */
  509. void() PutClientInServer =
  510. {
  511.     local   entity spot;
  512.  
  513.     self.classname = "player";
  514.     self.health = 100;
  515.     self.takedamage = DAMAGE_AIM;
  516.     self.solid = SOLID_SLIDEBOX;
  517.     self.movetype = MOVETYPE_WALK;
  518.     self.show_hostile = 0;
  519.     self.max_health = 100;
  520.     self.flags = FL_CLIENT;
  521.     self.air_finished = time + 12;
  522.     self.dmg = 2;           // initial water damage
  523.     self.super_damage_finished = 0;
  524.     self.radsuit_finished = 0;
  525.     self.invisible_finished = 0;
  526.     self.invincible_finished = 0;
  527.     self.effects = 0;
  528.     self.invincible_time = 0;
  529.     self.staydeadtime = 0;
  530.     self.regen_time = 0;
  531.     self.rune_notice_time = 0;
  532.     self.sniper_ammo = 5;   // ThunderWalker: Only 5 sniper shots per life
  533.     self.onedrop = 1;   // ThunderWalker: One rune destroy per life
  534.     self.reload = 0;    // ThunderWalker: Stops sniper reload message from appearing after death
  535.     self.flares = 5;    // ThunderWalker: Fezzik gets his flares
  536.  
  537.     self.last_hurt_carrier = -10;
  538.  
  539.     DecodeLevelParms ();
  540.     
  541.     spot = SelectSpawnPoint ();
  542. //ZOID: Minimize chance of telefragging someone, from Johannes Plass
  543. //(plass@dipmza.physik.uni-mainz.de) ServerModules package
  544.     spot = TelefragSelectSpawnPoint(spot);
  545.  
  546.     W_SetCurrentAmmo ();
  547.  
  548.     self.attack_finished = time;
  549.     self.th_pain = player_pain;
  550.     self.th_die = PlayerDie;
  551.     
  552.     self.deadflag = DEAD_NO;
  553. // paustime is set by teleporters to keep the player from moving a while
  554.     self.pausetime = 0;
  555.     
  556. //  spot = SelectSpawnPoint ();
  557.  
  558.  
  559.     self.origin = spot.origin + '0 0 1';
  560.     self.angles = spot.angles;
  561.     self.fixangle = TRUE;       // turn this way immediately
  562.  
  563. // oh, this is a hack!
  564.     setmodel (self, "progs/eyes.mdl");
  565.     modelindex_eyes = self.modelindex;
  566.  
  567.     setmodel (self, "progs/player.mdl");
  568.  
  569.     modelindex_player = self.modelindex;
  570.  
  571.     setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
  572.     
  573.     self.view_ofs = '0 0 22';
  574.  
  575.     player_stand1 ();
  576.     
  577.     makevectors(self.angles);
  578.     spawn_tfog (self.origin + v_forward*20);
  579.  
  580.     spawn_tdeath (self.origin, self);
  581.  
  582.  
  583.         // grapple stuff
  584.         self.on_hook = FALSE;
  585.         self.hook_out = FALSE;
  586.         self.fire_held_down = FALSE;
  587. };
  588.  
  589.  
  590. /*
  591. =============================================================================
  592.  
  593.                 QUAKED FUNCTIONS
  594.  
  595. =============================================================================
  596. */
  597.  
  598.  
  599. /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24)
  600. The normal starting point for a level.
  601. */
  602. void() info_player_start =
  603. {
  604. };
  605.  
  606.  
  607. /*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24)
  608. Only used on start map for the return point from an episode.
  609. */
  610. void() info_player_start2 =
  611. {
  612. };
  613.  
  614. /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24)
  615. potential spawning position for deathmatch games
  616. */
  617. void() info_player_deathmatch =
  618. {
  619.     if (deathmatch)
  620.         StartRuneSpawn();
  621. };
  622.  
  623. /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24)
  624. potential spawning position for coop games
  625. */
  626. void() info_player_coop =
  627. {
  628. };
  629.  
  630. /*
  631. ===============================================================================
  632.  
  633. RULES
  634.  
  635. ===============================================================================
  636. */
  637.  
  638. /*
  639. go to the next level for deathmatch
  640. only called if a time or frag limit has expired
  641. */
  642. void() NextLevel =
  643. {
  644.     local entity o;
  645.  
  646.     // episode one
  647.     if (mapname == "e1m1") nextmap = "e1m2";
  648.     else if (mapname == "e1m2") nextmap = "e1m3";
  649.     else if (mapname == "e1m3") nextmap = "e1m4";
  650.     else if (mapname == "e1m4") nextmap = "e1m5";
  651.     else if (mapname == "e1m5") nextmap = "e1m6";
  652.     else if (mapname == "e1m6") nextmap = "twstart";
  653.  
  654.     // episode two
  655.     else if (mapname == "e2m1") nextmap = "e2m2";
  656.     else if (mapname == "e2m2") nextmap = "e2m3";
  657.     else if (mapname == "e2m3") nextmap = "e2m4";
  658.     else if (mapname == "e2m4") nextmap = "e2m5";
  659.     else if (mapname == "e2m5") nextmap = "e2m6";
  660.     else if (mapname == "e2m6") nextmap = "e2m7";
  661.     else if (mapname == "e2m7") nextmap = "twstart";
  662.  
  663.     // episode three
  664.     else if (mapname == "e3m1") nextmap = "e3m2";
  665.     else if (mapname == "e3m2") nextmap = "e3m3";
  666.     else if (mapname == "e3m3") nextmap = "e3m4";
  667.     else if (mapname == "e3m4") nextmap = "e3m5";
  668.     else if (mapname == "e3m5") nextmap = "e3m6";
  669.     else if (mapname == "e3m6") nextmap = "e3m7";
  670.     else if (mapname == "e3m7") nextmap = "twstart";
  671.  
  672.     // episode four
  673.     else if (mapname == "e4m1") nextmap = "e4m2";
  674.     else if (mapname == "e4m2") nextmap = "e4m3";
  675.     else if (mapname == "e4m3") nextmap = "e4m4";
  676.     else if (mapname == "e4m4") nextmap = "e4m5";
  677.     else if (mapname == "e4m5") nextmap = "e4m6";
  678.     else if (mapname == "e4m6") nextmap = "e4m7";
  679.     else if (mapname == "e4m7") nextmap = "e4m8";
  680.     else if (mapname == "e4m8") nextmap = "twstart";
  681.  
  682.     // the deathmatch arenas
  683.     else if (mapname == "dm1") nextmap = "dm2";
  684.     else if (mapname == "dm2") nextmap = "dm3";
  685.     else if (mapname == "dm3") nextmap = "dm4";
  686.     else if (mapname == "dm4") nextmap = "dm5";
  687.     else if (mapname == "dm5") nextmap = "dm6";
  688.     else if (mapname == "dm6") nextmap = "twstart";
  689.  
  690.     // ctf episode one
  691.     else if (mapname == "ctf1") nextmap = "ctf2";
  692.     else if (mapname == "ctf2") nextmap = "ctf3";
  693.     else if (mapname == "ctf3") nextmap = "ctf4";
  694.     else if (mapname == "ctf4") nextmap = "ctf5";
  695.     else if (mapname == "ctf5") nextmap = "ctf6";
  696.     else if (mapname == "ctf6") nextmap = "ctf7";
  697.     else if (mapname == "ctf7") nextmap = "ctf8";
  698.     else if (mapname == "ctf8") nextmap = "twstart";
  699.  
  700.     // ctf episode two
  701.     else if (mapname == "ctf2m1") nextmap = "ctf2m2";
  702.     else if (mapname == "ctf2m2") nextmap = "ctf2m3";
  703.     else if (mapname == "ctf2m3") nextmap = "ctf2m4";
  704.     else if (mapname == "ctf2m4") nextmap = "ctf2m5";
  705.     else if (mapname == "ctf2m5") nextmap = "ctf2m6";
  706.     else if (mapname == "ctf2m6") nextmap = "ctf2m7";
  707.     else if (mapname == "ctf2m7") nextmap = "ctf2m8";
  708.     else if (mapname == "ctf2m8") nextmap = "twstart";
  709.  
  710.     // ThunderWalker episode
  711.     else if (mapname == "tnyctf3x") nextmap = "warz";
  712.     else if (mapname == "warz") nextmap = "gadiantn";
  713.     else if (mapname == "gadiantn") nextmap = "critctf1";
  714.     else if (mapname == "critctf1") nextmap = "twstart";
  715.  
  716.     o = spawn();
  717.     o.map = nextmap;
  718.     o.think = execute_changelevel;
  719.     o.nextthink = time + 0.1;
  720.     return;
  721.  
  722. // DISABLED from here
  723.  
  724.     // find a trigger changelevel
  725.     o = find(world, classname, "trigger_changelevel");
  726.  
  727.     // go back to start if no trigger_changelevel
  728.     if (!o)
  729.     {
  730.         mapname = "start";
  731.         o = spawn();
  732.         o.map = mapname;
  733.     }
  734.  
  735.     nextmap = o.map;
  736.     gameover = TRUE;
  737.     
  738.     if (o.nextthink < time)
  739.     {
  740.         o.think = execute_changelevel;
  741.         o.nextthink = time + 0.1;
  742.     }
  743.  
  744. };
  745.  
  746. /*
  747. ============
  748. CheckRules
  749.  
  750. Exit deathmatch games upon conditions
  751. ============
  752. */
  753. void() CheckRules =
  754. {
  755.     local   float       timelimit;
  756.     local   float       fraglimit;
  757.     local   entity      o;
  758.  
  759.     if (gameover || pregameover)    // someone else quit the game already
  760.         return;
  761.  
  762.     if (gamestart) {
  763.         if ((vote_leader != world) && voteexit_time && (time > voteexit_time)) {
  764.             pregameover = 1;
  765.             o = spawn();
  766.             nextmap = vote_leader.map;
  767.             o.map = nextmap;
  768.             o.think = execute_changelevel;
  769.             o.nextthink = time + 0.1;
  770.             return;
  771.         }
  772.         return;
  773.     }
  774.  
  775.     timelimit = cvar("timelimit") * 60;
  776.     fraglimit = cvar("fraglimit");
  777.     
  778.     if ((timelimit && time >= timelimit) ||
  779.         (fraglimit && (self.frags >= fraglimit))) {
  780.         pregameover = 1;
  781.         TeamEndScore();
  782.         NextLevel ();
  783.         return;
  784.     }
  785.  
  786. };
  787.  
  788.  
  789. //============================================================================
  790.  
  791. void() PlayerDeathThink =
  792. {
  793.     local entity    old_self;
  794.     local float     forward;
  795.  
  796.     if ((self.flags & FL_ONGROUND))
  797.     {
  798.         forward = vlen (self.velocity);
  799.         forward = forward - 20;
  800.         if (forward <= 0)
  801.             self.velocity = '0 0 0';
  802.         else    
  803.             self.velocity = forward * normalize(self.velocity);
  804.     }
  805.  
  806. // wait for all buttons released
  807.     if (self.deadflag == DEAD_DEAD)
  808.     {
  809.         if (self.button2 || self.button1 || self.button0)
  810.             return;
  811.         self.deadflag = DEAD_RESPAWNABLE;
  812.         return;
  813.     }
  814.  
  815. // wait for any button down
  816.     if (!self.button2 && !self.button1 && !self.button0)
  817.         return;
  818.  
  819.     self.button0 = 0;
  820.     self.button1 = 0;
  821.     self.button2 = 0;
  822.     respawn();
  823. };
  824.  
  825.  
  826. void() PlayerJump =
  827. {
  828.     local vector start, end;
  829.  
  830.     if (self.flags & FL_WATERJUMP)
  831.         return;
  832.     
  833.     if (self.waterlevel >= 2)
  834.     {
  835. // play swiming sound
  836.         if (self.swim_flag < time)
  837.         {
  838.             self.swim_flag = time + 1;
  839.             if (random() < 0.5)
  840.                                 sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM);
  841.             else
  842.                 sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM);
  843.         }
  844.  
  845.         return;
  846.     }
  847.  
  848.     if (!(self.flags & FL_ONGROUND))
  849.         return;
  850.  
  851.     if ( !(self.flags & FL_JUMPRELEASED) )
  852.         return;     // don't pogo stick
  853.  
  854.     self.flags = self.flags - (self.flags & FL_JUMPRELEASED);   
  855.     self.button2 = 0;
  856.  
  857. // player jumping sound
  858.     sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
  859. };
  860.  
  861.  
  862. /*
  863. ===========
  864. WaterMove
  865.  
  866. ============
  867. */
  868. .float  dmgtime;
  869.  
  870. void() WaterMove =
  871. {
  872. //dprint (ftos(self.waterlevel));
  873.     if (self.movetype == MOVETYPE_NOCLIP)
  874.         return;
  875.     if (self.health < 0)
  876.         return;
  877.  
  878. // ThunderWalker: makes visible in water with cloaking rune
  879.  
  880.         if (self.waterlevel >= 1 && (self.player_flag & ITEM_RUNE8_FLAG))
  881.     {
  882.         if (self.floyd == 1)
  883.         {
  884.         sound (self, CHAN_AUTO, "tw/twcloh2o.wav", 1, ATTN_NORM);
  885.         stuffcmd (self, "bf\n");
  886.         self.floyd = 2;
  887.         }
  888.     
  889.     setmodel (self, "progs/player.mdl");
  890.     self.bippy = time + 7;
  891.     self.gene = time + 9;
  892.     self.jimmy = 2;
  893.     }
  894.     
  895.  
  896.     if (self.waterlevel != 3)
  897.     {
  898.         if (self.air_finished < time)
  899.             sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM);
  900.         else if (self.air_finished < time + 9)
  901.             sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM);
  902.         self.air_finished = time + 12;
  903.         self.dmg = 2;
  904.     }
  905.     // ThunderWalker: Protection Rune prevents drowning 
  906.     else if (self.air_finished < time && !(self.player_flag & ITEM_RUNE6_FLAG))
  907.     {   // drown!
  908.         if (self.pain_finished < time)
  909.         {
  910.             self.dmg = self.dmg + 2;
  911.             if (self.dmg > 15)
  912.                 self.dmg = 10;
  913.             T_Damage (self, world, world, self.dmg);
  914.             self.pain_finished = time + 1;
  915.         }
  916.     }
  917.     
  918.     if (!self.waterlevel)
  919.     {
  920.         if (self.flags & FL_INWATER)
  921.         {   
  922.             // play leave water sound
  923.             sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM);
  924.             self.flags = self.flags - FL_INWATER;
  925.         }
  926.         return;
  927.     }
  928.  
  929.     // ThunderWalker: Protection Rune slows lava damage greatly
  930.     if (self.watertype == CONTENT_LAVA  && !(self.player_flag & ITEM_RUNE6_FLAG))
  931.     {   // do damage
  932.         if (self.dmgtime < time)
  933.         {
  934.             if (self.radsuit_finished > time)
  935.                 self.dmgtime = time + 1;
  936.             else
  937.                 self.dmgtime = time + 0.2;
  938.  
  939.             T_Damage (self, world, world, 10*self.waterlevel);
  940.         }
  941.     }
  942.     // ThunderWalker: Protection Rune protects from slime damage
  943.     else if (self.watertype == CONTENT_SLIME && !(self.player_flag & ITEM_RUNE6_FLAG))
  944.     {   // do damage
  945.         if (self.dmgtime < time && self.radsuit_finished < time)
  946.         {
  947.             self.dmgtime = time + 1;
  948.             T_Damage (self, world, world, 4*self.waterlevel);
  949.         }
  950.     }
  951.     
  952.     if ( !(self.flags & FL_INWATER) )
  953.     {   
  954.  
  955. // player enter water sound
  956.  
  957.         if (self.watertype == CONTENT_LAVA)
  958.             sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM);
  959.         if (self.watertype == CONTENT_WATER)
  960.             sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
  961.         if (self.watertype == CONTENT_SLIME)
  962.             sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
  963.  
  964.         self.flags = self.flags + FL_INWATER;
  965.         self.dmgtime = 0;
  966.     }   
  967. };
  968.  
  969. void() CheckWaterJump =
  970. {
  971.     local vector start, end;
  972.  
  973. // check for a jump-out-of-water
  974.     makevectors (self.angles);
  975.     start = self.origin;
  976.     start_z = start_z + 8; 
  977.     v_forward_z = 0;
  978.     normalize(v_forward);
  979.     end = start + v_forward*24;
  980.     traceline (start, end, TRUE, self);
  981.     if (trace_fraction < 1)
  982.     {   // solid at waist
  983.         start_z = start_z + self.maxs_z - 8;
  984.         end = start + v_forward*24;
  985.         self.movedir = trace_plane_normal * -50;
  986.         traceline (start, end, TRUE, self);
  987.         if (trace_fraction == 1)
  988.         {   // open at eye level
  989.             self.flags = self.flags | FL_WATERJUMP;
  990.             self.velocity_z = 225;
  991.             self.flags = self.flags - (self.flags & FL_JUMPRELEASED);
  992.             self.teleport_time = time + 2;  // safety net
  993.             return;
  994.         }
  995.     }
  996.  
  997. };
  998.  
  999.  
  1000. /*
  1001. ================
  1002. PlayerPreThink
  1003.  
  1004. Called every frame before physics are run
  1005. ================
  1006. */
  1007. void() PlayerPreThink =
  1008. {
  1009.     local   float   mspeed, aspeed;
  1010.     local   float   r;
  1011.  
  1012.     if (intermission_running > 0)
  1013.     {
  1014.         IntermissionThink ();   // otherwise a button could be missed between
  1015.         return;                 // the think tics
  1016.     }
  1017.  
  1018.     TeamCapturePlayerUpdate();
  1019.  
  1020.     if (self.view_ofs == '0 0 0')
  1021.         return;     // intermission or finale
  1022.  
  1023.     makevectors (self.v_angle);     // is this still used
  1024.  
  1025.     CheckRules ();
  1026.     WaterMove ();
  1027.  
  1028. // *TEAMPLAY*
  1029. // TeamCheckLock performs all necessary teamlock checking, and performs all
  1030. // actions needed.
  1031.     TeamCheckLock();
  1032.  
  1033. /*
  1034.     if (self.waterlevel == 2)
  1035.         CheckWaterJump ();
  1036. */
  1037.  
  1038.     if (self.deadflag >= DEAD_DEAD)
  1039.     {
  1040.         PlayerDeathThink ();
  1041.         return;
  1042.     }
  1043.     
  1044.     if (self.deadflag == DEAD_DYING)
  1045.         return; // dying, so do nothing
  1046.  
  1047.     if (self.button2)
  1048.     {
  1049.         PlayerJump ();
  1050.     }
  1051.     else
  1052.         self.flags = self.flags | FL_JUMPRELEASED;
  1053.  
  1054. // teleporters can force a non-moving pause time    
  1055.     if (time < self.pausetime)
  1056.         self.velocity = '0 0 0';
  1057.  
  1058.  
  1059. /* ThunderWalker:  We split this rune into regen health and living armor
  1060. // RUNE: If player has rune of elder magic (4), regeneration
  1061.     if (self.player_flag & ITEM_RUNE4_FLAG) {
  1062.         if (self.regen_time < time) {
  1063.             self.regen_time = time;
  1064.             if (self.health < 150) {
  1065.                 self.health = self.health + 5;
  1066.                 if (self.health > 150)
  1067.                     self.health = 150;
  1068.                 self.regen_time = self.regen_time + 0.5;
  1069.                 RegenerationSound();
  1070.             }
  1071.             if (self.armorvalue < 150 && self.armortype) {
  1072.                 self.armorvalue = self.armorvalue + 5;
  1073.                 if (self.armorvalue > 150)
  1074.                     self.armorvalue = 150;
  1075.                 self.regen_time = self.regen_time + 0.5;
  1076.                 RegenerationSound();
  1077.             }
  1078.         }
  1079.     }
  1080. */
  1081. // ThunderWalker: Zoid's old regen code from 3.5 modified to kick up to 200
  1082. // RUNE: If player has rune of elder magic (4), regeneration
  1083.     if (self.player_flag & ITEM_RUNE4_FLAG) {
  1084.         if (self.health < 200 && self.regen_time < time) {
  1085.             self.health = self.health + 3;
  1086.             self.regen_time = time + 0.5;
  1087.             RegenerationSound();
  1088.         }
  1089.     }
  1090. // RUNE
  1091. // ThunderWalker Rune: the rune of living armor.  Regens armor to 250 points.
  1092.     if (self.player_flag & ITEM_RUNE5_FLAG)
  1093.     {
  1094.         if (self.armorvalue < 250 && self.regen_time < time)
  1095.         {
  1096.             self.armorvalue = self.armorvalue + 5;
  1097.             if (self.armorvalue > 250)
  1098.                 self.armorvalue = 250;
  1099.             self.regen_time = time + 0.5;
  1100.             RegenerationSound();
  1101.         }
  1102.     }
  1103.  
  1104. // ThunderWalker rune: Protection, this makes you take damage slowly in lava.
  1105.  
  1106.     if ((self.player_flag & ITEM_RUNE6_FLAG) && self.watertype == CONTENT_LAVA)
  1107.     {
  1108.         if (self.dmgtime < time)
  1109.         {
  1110.         self.dmgtime = time + 1;
  1111.         T_Damage (self, world, world, 3);
  1112.         }
  1113.     }
  1114.  
  1115. // RUNE
  1116.  
  1117.     if(time > self.attack_finished && self.currentammo == 0 && 
  1118.                 self.weapon != IT_AXE && self.weapon != IT_GRAPPLE && self.weapon != IT_AIRGUN)
  1119.     {
  1120.         self.weapon = W_BestWeapon ();
  1121.         W_SetCurrentAmmo ();
  1122.     }
  1123.  
  1124.         // Do grapple stuff if I'm on a hook
  1125.         if (self.on_hook)
  1126.                 Service_Grapple ();
  1127.  
  1128. };
  1129.     
  1130. /*
  1131. ================
  1132. CheckPowerups
  1133.  
  1134. Check for turning off powerups
  1135. ================
  1136. */
  1137. void() CheckPowerups =
  1138. {
  1139.     if (self.health <= 0)
  1140.         return;
  1141.  
  1142. // invisibility
  1143.     if (self.invisible_finished)
  1144.     {
  1145. // sound and screen flash when items starts to run out
  1146.         if (self.invisible_sound < time)
  1147.         {
  1148.             sound (self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE);
  1149.             self.invisible_sound = time + ((random() * 3) + 1);
  1150.         }
  1151.  
  1152.         if (self.invisible_finished < time + 3)
  1153.         {
  1154.             if (self.invisible_time == 1)
  1155.             {
  1156.                 sprint (self, PRINT_HIGH, "Ring of Shadows magic is fading\n");
  1157.                 stuffcmd (self, "bf\n");
  1158.                 sound (self, CHAN_AUTO, "items/inv2.wav", 1, ATTN_NORM);
  1159.                 self.invisible_time = time + 1;
  1160.             }
  1161.             
  1162.             if (self.invisible_time < time)
  1163.             {
  1164.                 self.invisible_time = time + 1;
  1165.                 stuffcmd (self, "bf\n");
  1166.             }
  1167.         }
  1168.  
  1169.         if (self.invisible_finished < time)
  1170.         {   // just stopped
  1171.             self.items = self.items - IT_INVISIBILITY;
  1172.             self.invisible_finished = 0;
  1173.             self.invisible_time = 0;
  1174.         }
  1175.         
  1176.     // use the eyes
  1177.         self.frame = 0;
  1178.         self.modelindex = modelindex_eyes;
  1179.     }
  1180.     else
  1181.         self.modelindex = modelindex_player;    // don't use eyes
  1182.  
  1183. // invincibility
  1184.     if (self.invincible_finished)
  1185.     {
  1186. // sound and screen flash when items starts to run out
  1187.         if (self.invincible_finished < time + 3)
  1188.         {
  1189.             if (self.invincible_time == 1)
  1190.             {
  1191.                 sprint (self, PRINT_HIGH, "Protection is almost burned out\n");
  1192.                 stuffcmd (self, "bf\n");
  1193.                 sound (self, CHAN_AUTO, "items/protect2.wav", 1, ATTN_NORM);
  1194.                 self.invincible_time = time + 1;
  1195.             }
  1196.             
  1197.             if (self.invincible_time < time)
  1198.             {
  1199.                 self.invincible_time = time + 1;
  1200.                 stuffcmd (self, "bf\n");
  1201.             }
  1202.         }
  1203.         
  1204.         if (self.invincible_finished < time)
  1205.         {   // just stopped
  1206.             self.items = self.items - IT_INVULNERABILITY;
  1207.             self.invincible_time = 0;
  1208.             self.invincible_finished = 0;
  1209.         }
  1210. // ZOID, next line isn't needed, EF_DIMLIGHT is handled by
  1211. // client.qc:CheckDimLight
  1212. //      if (self.invincible_finished > time)
  1213. //          self.effects = self.effects | EF_DIMLIGHT;
  1214. //      else
  1215. //          self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  1216.     }
  1217.  
  1218. // super damage
  1219.     if (self.super_damage_finished)
  1220.     {
  1221.  
  1222. // sound and screen flash when items starts to run out
  1223.  
  1224.         if (self.super_damage_finished < time + 3)
  1225.         {
  1226.             if (self.super_time == 1)
  1227.             {
  1228.                 sprint (self, PRINT_HIGH, "Quad Damage is wearing off\n");
  1229.                 stuffcmd (self, "bf\n");
  1230.                 sound (self, CHAN_AUTO, "items/damage2.wav", 1, ATTN_NORM);
  1231.                 self.super_time = time + 1;
  1232.             }     
  1233.             
  1234.             if (self.super_time < time)
  1235.             {
  1236.                 self.super_time = time + 1;
  1237.                 stuffcmd (self, "bf\n");
  1238.             }
  1239.         }
  1240.  
  1241.         if (self.super_damage_finished < time)
  1242.         {   // just stopped
  1243.             self.items = self.items - IT_QUAD;
  1244.             self.super_damage_finished = 0;
  1245.             self.super_time = 0;
  1246.         }
  1247. // ZOID, next line isn't needed, EF_DIMLIGHT is handled by
  1248. // client.qc:CheckDimLight
  1249. //      if (self.super_damage_finished > time)
  1250. //          self.effects = self.effects | EF_DIMLIGHT;
  1251. //      else
  1252. //          self.effects = self.effects - (self.effects & EF_DIMLIGHT);
  1253.     }   
  1254.  
  1255. // suit 
  1256.     if (self.radsuit_finished)
  1257.     {
  1258.         self.air_finished = time + 12;      // don't drown
  1259.  
  1260. // sound and screen flash when items starts to run out
  1261.         if (self.radsuit_finished < time + 3)
  1262.         {
  1263.             if (self.rad_time == 1)
  1264.             {
  1265.                 sprint (self, PRINT_HIGH, "Air supply in Biosuit expiring\n");
  1266.                 stuffcmd (self, "bf\n");
  1267.                 sound (self, CHAN_AUTO, "items/suit2.wav", 1, ATTN_NORM);
  1268.                 self.rad_time = time + 1;
  1269.             }
  1270.             
  1271.             if (self.rad_time < time)
  1272.             {
  1273.                 self.rad_time = time + 1;
  1274.                 stuffcmd (self, "bf\n");
  1275.             }
  1276.         }
  1277.  
  1278.         if (self.radsuit_finished < time)
  1279.         {   // just stopped
  1280.             self.items = self.items - IT_SUIT;
  1281.             self.rad_time = 0;
  1282.             self.radsuit_finished = 0;
  1283.         }
  1284.     }   
  1285.  
  1286.     // Check to see about DIMLIGHT effects
  1287.     CheckDimLight();
  1288. };
  1289.  
  1290.  
  1291. /*
  1292. ================
  1293. PlayerPostThink
  1294.  
  1295. Called every frame after physics are run
  1296. ================
  1297. */
  1298. void() PlayerPostThink =
  1299. {
  1300.     local   float   mspeed, aspeed;
  1301.     local   float   r;
  1302.  
  1303. //dprint ("post think\n");
  1304.     if (self.view_ofs == '0 0 0')
  1305.         return;     // intermission or finale
  1306.     if (self.deadflag)
  1307.         return;
  1308.  
  1309. // check to see if player landed and play landing sound 
  1310.     if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) )
  1311.     {
  1312.         if (self.watertype == CONTENT_WATER)
  1313.             sound (self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
  1314.         else if (self.jump_flag < -650)
  1315.         {
  1316.             T_Damage (self, world, world, 5); 
  1317.             sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM);
  1318.             self.deathtype = "falling";
  1319.         }
  1320.         else
  1321.             sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM);
  1322.     }
  1323.  
  1324.     self.jump_flag = self.velocity_z;
  1325.  
  1326.     CheckPowerups ();
  1327.  
  1328.     W_WeaponFrame ();
  1329.  
  1330. };
  1331.  
  1332.  
  1333. /*
  1334. ===========
  1335. ClientConnect
  1336.  
  1337. called when a player connects to a server
  1338. ============
  1339. */
  1340. void() ClientConnect =
  1341. {
  1342.     bprint (PRINT_HIGH, self.netname);
  1343.     bprint (PRINT_HIGH, " entered the game\n");
  1344.     
  1345.     self.motd_count = 1;
  1346.     self.player_connect = self.player_connect - (self.player_connect & PF_GHOST);
  1347.  
  1348.     self.suicide_count = 0;
  1349.     self.killed = 0;
  1350.     self.player_flag = 0;
  1351.     
  1352. // *TEAMPLAY*
  1353.     // If this is our first connection, parm10 is < 0
  1354.     // Set lastteam negative.
  1355.     if (parm10 < 0 || self.steam < 0) {
  1356.         self.steam = -1;
  1357.         TeamAssign();
  1358.         if(teamplay & TEAM_LOCK_COLORS) // force a stuff cmd in think
  1359.             self.player_connect = self.player_connect | TEAM_STUFF_COLOR;
  1360.     }
  1361.  
  1362.  
  1363. // a client connecting during an intermission can cause problems
  1364.     if (intermission_running)
  1365.         GotoNextMap ();
  1366. };
  1367.  
  1368.  
  1369. /*
  1370. ===========
  1371. ClientDisconnect
  1372.  
  1373. called when a player disconnects from a server
  1374. ============
  1375. */
  1376. void() ClientDisconnect =
  1377. {
  1378.     // let everyone else know
  1379.     bprint (PRINT_HIGH, self.netname);
  1380.     bprint (PRINT_HIGH, " left the game with ");
  1381.     bprint (PRINT_HIGH, ftos(self.frags));
  1382.     bprint (PRINT_HIGH, " frags\n");
  1383.     sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
  1384.     DropRune();
  1385.     TeamCaptureDropFlagOfPlayer(self);
  1386.     set_suicide_frame ();
  1387.  
  1388.     self.player_connect = self.player_connect | PF_GHOST;
  1389.     self.steam = -1;
  1390.     self.frags = 0;
  1391.     self.statstate = 0;
  1392. };
  1393.  
  1394. // *TEAMPLAY*
  1395. // Prototypes
  1396.  
  1397. float(entity targ, entity attacker) TeamFragPenalty;
  1398. void(entity targ, entity attacker) TeamDeathPenalty;
  1399.  
  1400. /*
  1401. ===========
  1402. ClientObituary
  1403.  
  1404. called when a player dies
  1405. ============
  1406. */
  1407. void(entity targ, entity attacker) ClientObituary =
  1408. {
  1409.  
  1410.     // *XXX* EXPERT CTF variable for 
  1411.     // flag/flag carrier defense bonus determination
  1412.     local   entity head;
  1413.     local float flag_radius;
  1414.     local float flag_carrier_radius;
  1415.  
  1416.         local   float rnum, temp;
  1417.     local   string deathstring, deathstring2, s;
  1418.     rnum = random();
  1419.  
  1420.     if (targ.classname == "player")
  1421.     {
  1422.  
  1423.         // *XXX* EXPERT CTF: 
  1424.         // When the flag carrier dies, reset the last_hurt_carrier field in
  1425.         // all players on the opposite team from the flag carrier.  The carrier
  1426.         // has been killed, so there is no longer a reason to award points for
  1427.         // killing off his assailants
  1428.         if (targ.player_flag & ITEM_ENEMY_FLAG) {
  1429.  
  1430.             head = find(world, classname, "player");
  1431.  
  1432.             while (head != world) { 
  1433.                 if (head.steam != targ.steam)
  1434.                     head.last_hurt_carrier = -10;
  1435.                 head = find(head, classname, "player");
  1436.             }
  1437.         }
  1438.         // END EXPERT CTF
  1439.  
  1440.         if (attacker.classname == "teledeath")
  1441.         {
  1442.             bprint (PRINT_MEDIUM,targ.netname);
  1443.             bprint (PRINT_MEDIUM," was telefragged by ");
  1444.             bprint (PRINT_MEDIUM,attacker.owner.netname);
  1445.             bprint (PRINT_MEDIUM,"\n");
  1446.  
  1447.             attacker.owner.frags = attacker.owner.frags + 1;
  1448.             return;
  1449.         }
  1450.  
  1451.         if (attacker.classname == "teledeath2")
  1452.         {
  1453.             bprint (PRINT_MEDIUM,"Satan's power deflects ");
  1454.             bprint (PRINT_MEDIUM,targ.netname);
  1455.             bprint (PRINT_MEDIUM,"'s telefrag\n");
  1456.  
  1457.             targ.frags = targ.frags - 1;
  1458.             logfrag (targ, targ);
  1459.             return;
  1460.         }
  1461.  
  1462.         if (attacker.classname == "player")
  1463.         {
  1464.             if (targ == attacker)
  1465.             {
  1466.                 // killed self
  1467.                 logfrag (attacker, attacker);
  1468.                 attacker.frags = attacker.frags - 1;
  1469.                 bprint (PRINT_MEDIUM,targ.netname);
  1470.  
  1471.                 if (self.killed == 99) {
  1472.                     //ZOID: try if player was gibbed for changing teams
  1473.                     if (teamplay & TEAM_STATIC_TEAMS)
  1474.                         bprint (PRINT_MEDIUM, " tried to change teams\n");
  1475.                     else
  1476.                         bprint (PRINT_MEDIUM, " changed teams\n");
  1477.                 } else if (targ.weapon == 64 && targ.waterlevel > 1) {
  1478.                     bprint (PRINT_MEDIUM," discharges into the water.\n");
  1479.                     return;
  1480.                 } else if (targ.weapon == IT_GRENADE_LAUNCHER) {
  1481.                     bprint (PRINT_MEDIUM," tries to put the pin back in\n");
  1482.                 } else if (rnum) {
  1483.                     bprint (PRINT_MEDIUM," becomes bored with life\n");
  1484.                 } else {
  1485.                     bprint (PRINT_MEDIUM," checks if his weapon is loaded\n");
  1486.                 }
  1487.                 return;
  1488.             }
  1489.             else
  1490.             {
  1491.                 // *TEAMPLAY*
  1492.                 // TeamFragPenalty returns true if the attacker gets a frag penalty for
  1493.                 // killing this target.  It also deducts frags as needed.
  1494.                 if (!TeamFragPenalty(targ, attacker)) {
  1495.  
  1496.                     // the attacker is award the normal one frag.. now we 
  1497.                     // determine if he gets any bonuses
  1498.                     logfrag (attacker, targ);
  1499.                     attacker.frags = attacker.frags + 1;
  1500.    
  1501.                     if ((targ.player_flag & ITEM_ENEMY_FLAG) &&
  1502.                         (targ.steam != attacker.steam)) {
  1503.                         //ZOID: one team fragged the other team's flag carrier
  1504.  
  1505.                         // *XXX* EXPERT CTF
  1506.                         // Mark the attacker with the time at which he killed the flag
  1507.                         // carrier, for awarding assist points
  1508.  
  1509.                         attacker.last_fragged_carrier = time;
  1510.  
  1511.                         // *XXX* EXPERT CTF: give player only the normal amount of frags
  1512.                         // if the carrier has only had the flag for a few seconds, to
  1513.                         // prevent ppl intentionally allowing enemies to grab the flag,
  1514.                         // then immediately fragging them
  1515.  
  1516.                         if (targ.flag_since + TEAM_CAPTURE_CARRIER_FLAG_SINCE_TIMEOUT > time) {
  1517.                             sprint(attacker, PRINT_MEDIUM, "Enemy flag carrier killed, no bonus\n");
  1518.                         } else {
  1519.                             attacker.frags = attacker.frags + TEAM_CAPTURE_FRAG_CARRIER_BONUS;
  1520.                             sprint(attacker, PRINT_MEDIUM, "Enemy flag carrier killed: ");
  1521.                             s = ftos(TEAM_CAPTURE_FRAG_CARRIER_BONUS);
  1522.                             sprint(attacker, PRINT_MEDIUM, s);
  1523.                             sprint(attacker, PRINT_MEDIUM, " bonus frags\n");
  1524.                         }
  1525.                         // END FLAG CARRIER FRAG CODE
  1526.                     }
  1527.                     
  1528.                     // *XXX* EXPERT CTF
  1529.                     // This code checks for all game-critical kills OTHER THAN fragging the enemy
  1530.                     // flag carrier, like killing players who are trying to kill your flag carrier
  1531.                     // or trying to grab your flag, and hands out bonus frags.
  1532.  
  1533.                     // The two variables below track whether special bonus frags have already
  1534.                     // been awarded for the attacker or target being near the flag or flag carrier.  
  1535.  
  1536.                     flag_radius = 0;
  1537.                     flag_carrier_radius = 0;
  1538.  
  1539.                     // get a string for the attacker's team now, for later announcements
  1540.                     s = GetCTFTeam(attacker.steam);
  1541.  
  1542.                     if ((targ.last_hurt_carrier + TEAM_CAPTURE_CARRIER_DANGER_PROTECT_TIMEOUT > time) &&
  1543.                         !(attacker.player_flag & ITEM_ENEMY_FLAG) ) {
  1544.                         // a player on the same team as the flag carrier killed 
  1545.                         // someone who recently shot the flag carrier
  1546.                         attacker.frags = attacker.frags + 
  1547.                             TEAM_CAPTURE_CARRIER_DANGER_PROTECT_BONUS;
  1548.                         flag_carrier_radius = 1;
  1549.                         // NOTE: getting CARRIER_DANGER_PROTECT_BONUS precludes getting
  1550.                         // other kinds of bonuses for defending the flag carrier, since
  1551.                         // it's worth more points
  1552.                         bprint(PRINT_MEDIUM, attacker.netname);
  1553.                         bprint(PRINT_MEDIUM, " defends ");
  1554.                         bprint(PRINT_MEDIUM, s);
  1555.                         bprint(PRINT_MEDIUM, "'s flag carrier against an agressive enemy\n");
  1556.                     }
  1557.  
  1558.                     // *XXX* EXPERT CTF
  1559.                     // Bonusus for defending the flag carrier or the flag itself.
  1560.                     // Extra frags are awarded if either the attacker or the target are
  1561.                     // 1. within 40 feet of a flag carrier on the same team as the attacker
  1562.                     // 2. within 40 feet of the attacker's flag
  1563.                     // These bonuses are cumulative with respect to defending both the
  1564.                     // flag and the flag carrier at the same time, but not cumulative with
  1565.                     // respect to both the target and attacker being near the object being defended
  1566.  
  1567.                     // find flags or flag carriers within a radius of the attacker
  1568.                     head = findradius(attacker.origin, TEAM_CAPTURE_ATTACKER_PROTECT_RADIUS);
  1569.  
  1570.                     while (head) {
  1571.                         if (head.classname == "player") {
  1572.                             if ( (head.steam == attacker.steam) &&
  1573.                                  (head.player_flag & ITEM_ENEMY_FLAG) &&
  1574.                                  (head != attacker) && // self defense
  1575.                                  (!flag_carrier_radius) ) { 
  1576.                                 // attacker was near his own flag carrier
  1577.                                 attacker.frags = attacker.frags + 
  1578.                                     TEAM_CAPTURE_CARRIER_PROTECT_BONUS;
  1579.                                 flag_carrier_radius = 1;
  1580.                                 bprint(PRINT_MEDIUM, attacker.netname);
  1581.                                 bprint(PRINT_MEDIUM, " defends ");
  1582.                                 bprint(PRINT_MEDIUM, s);
  1583.                                 bprint(PRINT_MEDIUM, "'s flag carrier\n");
  1584.                             }
  1585.                         }
  1586.                         if ( (head.classname == "item_flag_team1") ||
  1587.                              (head.classname == "item_flag_team2")) {
  1588.                             if (((attacker.steam == TEAM_COLOR1) &&
  1589.                                 (head.classname == "item_flag_team1")) ||
  1590.                                 ((attacker.steam == TEAM_COLOR2) &&
  1591.                                 (head.classname == "item_flag_team2"))) { 
  1592.                                 // attacker was near his own flag
  1593.                                 attacker.frags = attacker.frags + 
  1594.                                     TEAM_CAPTURE_FLAG_DEFENSE_BONUS;
  1595.                                 flag_radius = 1; 
  1596.                                 bprint(PRINT_MEDIUM, attacker.netname);
  1597.                                 bprint(PRINT_MEDIUM, " defends the ");
  1598.                                 bprint(PRINT_MEDIUM, s);
  1599.                                 bprint(PRINT_MEDIUM, " flag\n");
  1600.                             }
  1601.                         }
  1602.                         head = head.chain;
  1603.                     }
  1604.  
  1605.                     // find flags or flag carriers within a radius from the target
  1606.                     head = findradius(targ.origin, TEAM_CAPTURE_TARGET_PROTECT_RADIUS);
  1607.                     while (head) {
  1608.                         if (head.classname == "player") {
  1609.                             if ( (head.steam == attacker.steam) &&
  1610.                                  (head.player_flag & ITEM_ENEMY_FLAG) &&
  1611.                                  (head != attacker) &&
  1612.                                  (!flag_carrier_radius)) { // prevents redundant points awarded
  1613.                                 // target was near attacker's flag carrier
  1614.                                 attacker.frags = attacker.frags + 
  1615.                                     TEAM_CAPTURE_CARRIER_PROTECT_BONUS;
  1616.                                 flag_carrier_radius = 1;
  1617.                                 bprint(PRINT_MEDIUM, attacker.netname);
  1618.                                 bprint(PRINT_MEDIUM, " defends ");
  1619.                                 bprint(PRINT_MEDIUM, s);
  1620.                                 bprint(PRINT_MEDIUM, "'s flag carrier\n");
  1621.                             }
  1622.                         }
  1623.                         if (((attacker.steam == TEAM_COLOR1) &&
  1624.                             (head.classname == "item_flag_team1")) ||
  1625.                             ((attacker.steam == TEAM_COLOR2) &&
  1626.                             (head.classname == "item_flag_team2"))
  1627.                             && (!flag_radius)) { // prevents redundant points awarded
  1628.                             // target was near attacker's flag
  1629.                             attacker.frags = attacker.frags + 
  1630.                                 TEAM_CAPTURE_FLAG_DEFENSE_BONUS;
  1631.                             flag_radius = 1;
  1632.                             bprint(PRINT_MEDIUM, attacker.netname);
  1633.                             bprint(PRINT_MEDIUM, " defends the ");
  1634.                             bprint(PRINT_MEDIUM, s);
  1635.                             bprint(PRINT_MEDIUM, " flag\n");
  1636.                         }
  1637.                         head = head.chain;
  1638.                     }
  1639.                 }   
  1640.              
  1641.                 // *XXX* EXPERT CTF 
  1642.                 // End frag determination code.  Now determine death text for
  1643.                 // a member of one team killing a member of the other
  1644.  
  1645.                 // *TEAMPLAY*
  1646.                 // TeamDeathPenalty kills the attacker if necessary and adjusts frags to
  1647.                 // offset the one frag penalty for dying.
  1648.                 TeamDeathPenalty(targ, attacker);
  1649.  
  1650.                 rnum = attacker.weapon;
  1651.                 if (rnum == IT_AXE)
  1652.                 {
  1653.                     deathstring = " was ax-murdered by ";
  1654.                     deathstring2 = "\n";
  1655.                 }
  1656.                                 if (rnum == IT_GRAPPLE)
  1657.                 {
  1658.                                         temp = random();
  1659.                                         if (temp < 0.5)
  1660.                                         {
  1661.                                                 deathstring = " was hooked by ";
  1662.                                                 deathstring2 = "\n";
  1663.                                         }
  1664.                                         else if (temp > 0.5)
  1665.                                         {
  1666.                                                 deathstring = " was disemboweled by ";
  1667.                                                 deathstring2 = "\n";
  1668.                                         }                                                
  1669.                     }
  1670.                 if (rnum == IT_SHOTGUN)
  1671.                 {
  1672.                     deathstring = " chewed on ";
  1673.                     deathstring2 = "'s boomstick\n";
  1674.                 }
  1675.                 if (rnum == IT_SUPER_SHOTGUN)
  1676.                 {
  1677.                     deathstring = " ate 2 loads of ";
  1678.                     deathstring2 = "'s buckshot\n";
  1679.                 }
  1680.                 if (rnum == IT_NAILGUN)
  1681.                 {
  1682.                     deathstring = " was lasered by ";
  1683.                     deathstring2 = "\n";
  1684.                 }
  1685.                 if (rnum == IT_SUPER_NAILGUN)
  1686.                 {
  1687.                     deathstring = " was punctured by ";
  1688.                     deathstring2 = "\n";
  1689.                 }
  1690.                 if (rnum == IT_GRENADE_LAUNCHER)
  1691.                 {
  1692.                     deathstring = " eats ";
  1693.                     deathstring2 = "'s pineapple\n";
  1694.                     if (targ.health < -40)
  1695.                     {
  1696.                         deathstring = " was gibbed by ";
  1697.                         deathstring2 = "'s grenade\n";
  1698.                     }
  1699.                 }
  1700.                 if (rnum == IT_ROCKET_LAUNCHER)
  1701.                 {
  1702.                     if (attacker.items & IT_QUAD) {
  1703.                         deathstring = " was destroyed by ";
  1704.                         deathstring2 = "'s Quad rocket\n";
  1705.                     } else {
  1706.                         deathstring = " rides ";
  1707.                         deathstring2 = "'s rocket\n";
  1708.                         if (targ.health < -40)
  1709.                         {
  1710.                             deathstring = " was gibbed by ";
  1711.                             deathstring2 = "'s rocket\n" ;
  1712.                         }
  1713.                     }
  1714.                 }
  1715.                 // AIRG_WEAPON_START
  1716.  
  1717. // Death messages for the AirFist.
  1718.  
  1719.                 if (rnum == IT_AIRGUN)
  1720.         {
  1721.                     if (targ.health < -40)
  1722.                     {
  1723.                         deathstring = " was gibbed by ";
  1724.                         deathstring2 = "'s wind!\n" ;
  1725.                     }
  1726.           else
  1727.           {
  1728.                         deathstring = " was blown by ";
  1729.                         deathstring2 = "\n" ;
  1730.           }
  1731.             return;
  1732.                 }
  1733. // AIRG_WEAPON_END
  1734.                 if (rnum == IT_LIGHTNING)
  1735.                 {
  1736.                     deathstring = " was snipified by ";
  1737.                     deathstring2 = "\n";
  1738.                 }
  1739.                 bprint (PRINT_MEDIUM,targ.netname);
  1740.                 bprint (PRINT_MEDIUM,deathstring);
  1741.                 bprint (PRINT_MEDIUM,attacker.netname);
  1742.                 bprint (PRINT_MEDIUM,deathstring2);
  1743.             }
  1744.             return;
  1745.         }
  1746.         else
  1747.         {
  1748.             logfrag (targ, targ);
  1749.             targ.frags = targ.frags - 1;        // killed self
  1750.             rnum = targ.watertype;
  1751.  
  1752.             bprint (PRINT_MEDIUM,targ.netname);
  1753.             if (rnum == -3)
  1754.             {
  1755.                 if (random() < 0.5)
  1756.                     bprint (PRINT_MEDIUM," sleeps with the fishes\n");
  1757.                 else
  1758.                     bprint (PRINT_MEDIUM," sucks it down\n");
  1759.                 return;
  1760.             }
  1761.             else if (rnum == -4)
  1762.             {
  1763.                 if (random() < 0.5)
  1764.                     bprint (PRINT_MEDIUM," gulped a load of slime\n");
  1765.                 else
  1766.                     bprint (PRINT_MEDIUM," can't exist on slime alone\n");
  1767.                 return;
  1768.             }
  1769.             else if (rnum == -5)
  1770.             {
  1771.                 if (targ.health < -15)
  1772.                 {
  1773.                     bprint (PRINT_MEDIUM," burst into flames\n");
  1774.                     return;
  1775.                 }
  1776.                 if (random() < 0.5)
  1777.                     bprint (PRINT_MEDIUM," turned into hot slag\n");
  1778.                 else
  1779.                     bprint (PRINT_MEDIUM," visits the Volcano God\n");
  1780.                 return;
  1781.             }
  1782.  
  1783.             if (attacker.flags & FL_MONSTER)
  1784.             {
  1785.                 if (attacker.classname == "monster_army")
  1786.                     bprint (PRINT_MEDIUM," was shot by a Grunt\n");
  1787.                 if (attacker.classname == "monster_demon1")
  1788.                     bprint (PRINT_MEDIUM," was eviscerated by a Fiend\n");
  1789.                 if (attacker.classname == "monster_dog")
  1790.                     bprint (PRINT_MEDIUM," was mauled by a Rottweiler\n");
  1791.                 if (attacker.classname == "monster_dragon")
  1792.                     bprint (PRINT_MEDIUM," was fried by a Dragon\n");
  1793.                 if (attacker.classname == "monster_enforcer")
  1794.                     bprint (PRINT_MEDIUM," was blasted by an Enforcer\n");
  1795.                 if (attacker.classname == "monster_fish")
  1796.                     bprint (PRINT_MEDIUM," was fed to the Rotfish\n");
  1797.                 if (attacker.classname == "monster_hell_knight")
  1798.                     bprint (PRINT_MEDIUM," was slain by a Death Knight\n");
  1799.                 if (attacker.classname == "monster_knight")
  1800.                     bprint (PRINT_MEDIUM," was slashed by a Knight\n");
  1801.                 if (attacker.classname == "monster_ogre")
  1802.                     bprint (PRINT_MEDIUM," was destroyed by an Ogre\n");
  1803.                 if (attacker.classname == "monster_oldone")
  1804.                     bprint (PRINT_MEDIUM," became one with Shub-Niggurath\n");
  1805.                 if (attacker.classname == "monster_shalrath")
  1806.                     bprint (PRINT_MEDIUM," was exploded by a Vore\n");
  1807.                 if (attacker.classname == "monster_shambler")
  1808.                     bprint (PRINT_MEDIUM," was smashed by a Shambler\n");
  1809.                 if (attacker.classname == "monster_tarbaby")
  1810.                     bprint (PRINT_MEDIUM," was slimed by a Spawn\n");
  1811.                 if (attacker.classname == "monster_vomit")
  1812.                     bprint (PRINT_MEDIUM," was vomited on by a Vomitus\n");
  1813.                 if (attacker.classname == "monster_wizard")
  1814.                     bprint (PRINT_MEDIUM," was scragged by a Scrag\n");
  1815.                 if (attacker.classname == "monster_zombie")
  1816.                     bprint (PRINT_MEDIUM," joins the Zombies\n");
  1817.  
  1818.                 return;
  1819.             }
  1820.             if (attacker.classname == "explo_box")
  1821.             {
  1822.                 bprint (PRINT_MEDIUM," blew up\n");
  1823.                 return;
  1824.             }
  1825.             if (attacker.solid == SOLID_BSP && attacker != world)
  1826.             {   
  1827.                 bprint (PRINT_MEDIUM," was squished\n");
  1828.                 return;
  1829.             }
  1830.             if (targ.deathtype == "falling")
  1831.             {
  1832.                 targ.deathtype = "";
  1833.                 bprint (PRINT_MEDIUM," fell to his death\n");
  1834.                 return;
  1835.             }
  1836.             if (attacker.classname == "trap_shooter" || attacker.classname == "trap_spikeshooter")
  1837.             {
  1838.                 bprint (PRINT_MEDIUM," was spiked\n");
  1839.                 return;
  1840.             }
  1841.             if (attacker.classname == "fireball")
  1842.             {
  1843.                 bprint (PRINT_MEDIUM," ate a lavaball\n");
  1844.                 return;
  1845.             }
  1846.             if (attacker.classname == "trigger_changelevel")
  1847.             {
  1848.                 bprint (PRINT_MEDIUM," tried to leave\n");
  1849.                 return;
  1850.             }
  1851.  
  1852.             bprint (PRINT_MEDIUM," died\n");
  1853.         }
  1854.     }
  1855. };
  1856.